﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Navigation;
using System.Windows.Threading;
using Microsoft.Phone.Controls;
using Samples.Radio.Agent;
using Samples.Radio.Data;
using Samples.Radio.Helpers;

namespace Samples.Radio.Data
{

    public class ViewModelBase:NotifyBase
    {
        public RadioRepository Repository { get; set; }
        public IDispatcher Dispatcher { get; protected set; }
        public INavigation Navigation { get; protected set; }

        public ViewModelBase(RadioRepository repository, IDispatcher dispatcher, INavigation navigation)
        {
            Repository = repository;
            Dispatcher = dispatcher;
            Navigation = navigation;

            Wirings = new List<EventWiring>();
        }



        private Dictionary<string, string> CurrentStates = new Dictionary<string, string>();

        public T CurrentState<T>() where T : struct
        {
            var current = CurrentStates.SafeDictionaryValue<string, string, string>(typeof(T).FullName);
            var tvalue = current.EnumParse<T>();
            return tvalue;
        }

        public event EventHandler<DualParameterEventArgs<string, bool>> StateChanged;

        public void ChangePageState<T>(T stateName, bool useTransitions = true) where T : struct
        {
            var current = CurrentStates.SafeDictionaryValue<string, string, string>(typeof(T).FullName);
            var newState = stateName.ToString();
            if (string.IsNullOrWhiteSpace(current) || current != newState)
            {
                StateChanged.SafeRaise(this, newState, useTransitions);
                CurrentStates[typeof(T).FullName] = newState;
            }
        }

        protected List<EventWiring> Wirings { get; private set; }

        protected class EventWiring
        {
            public Action Wire { get; set; }
            public Action Unwire { get; set; }
        }

        protected void AddEventWiring(Action wire, Action unwire)
        {
            Wirings.Add(new EventWiring() { Wire = wire, Unwire = unwire });
        }

        public virtual void OnNavigatedFrom(NavigationEventArgs navigationEventArgs,
                                            IDictionary<string, object> pageState)
        {
            Wirings.DoForEach(w => w.Unwire());

        }


        public virtual void OnNavigatedTo(NavigationEventArgs navigationEventArgs, NavigationContext context,
                                          IDictionary<string, object> pageState)
        {
            Wirings.DoForEach(w => w.Wire());
        }

        public virtual void OnNavigatingFrom(NavigatingCancelEventArgs navigatingCancelEventArgs,
                                             IDictionary<string, object> state)
        {

        }
    }

    public interface IDispatcher
    {
        /// <summary>
        /// Executes the action on the UI thread
        /// </summary>
        void BeginInvoke(Action action);

        /// <summary>
        /// Executes the delegate (with parameters) on the UI thread
        /// </summary>
        void BeginInvoke(Delegate d, params object[] args);

        bool CheckAccess();
    }

    public interface INavigation
    {
        bool CanGoBack { get; }
        void GoBack();
        void Navigate(Type page, params NavigationParameter[] parameters);
        void Navigate(Uri source);

        IEnumerable<JournalEntry> BackStack { get; }
        void RemoveBackEntry();

    }

    public class NavigationParameter
    {
        public string Key { get; set; }
        public string Value { get; set; }

        public NavigationParameter()
        {

        }

        public NavigationParameter(string key, string value)
        {
            Key = key;
            Value = value;
        }
        
    }

    internal static class NavigationEx
    {
        internal static INavigation AsInterface(this PhoneApplicationFrame phoneFrame)
        {
            return new NavigationWrapper(phoneFrame);
        }

        #region Nested type: NavigationWrapper

        private class NavigationWrapper : INavigation
        {
            public NavigationWrapper(PhoneApplicationFrame phoneFrame)
            {
                Service = phoneFrame;
            }

            private PhoneApplicationFrame Service { get; set; }

            #region INavigation Members


            public IEnumerable<JournalEntry> BackStack
            {
                get { return Service.BackStack; }
            }

            public void RemoveBackEntry()
            {
                Service.RemoveBackEntry();
            }

            public bool CanGoBack
            {
                get { return Service.CanGoBack; }
            }

            public void GoBack()
            {
                Service.GoBack();
            }

            public void Navigate(Uri source)
            {
                Service.Navigate(source);
            }

            public void Navigate(Type pageType, params NavigationParameter[] parameters)
            {


                var queryBuilder = new StringBuilder();
                foreach (var param in parameters)
                {
                    queryBuilder.Append(queryBuilder.Length == 0 ? "?" : "&");
                    queryBuilder.AppendFormat("{0}={1}", param.Key, Uri.EscapeDataString(param.Value));
                }
                var uri = string.Format("/{0}{1}",
                                        pageType.Name,
                                        queryBuilder);

                Service.Navigate(new Uri(uri, UriKind.Relative));
            }



            #endregion
        }

        #endregion
    }

    internal static class DispatcherEx
    {
        internal static IDispatcher AsInterface(this Dispatcher dispatcher)
        {
            return new DispatcherWrapper(dispatcher);
        }

        /// <summary>
        /// Wrapper class for the Dispatcher, implementing
        /// IContext (so that we can mock out the Dispatcher behaviour)
        /// </summary>
        private class DispatcherWrapper : IDispatcher
        {

            private Dispatcher Dispatcher { get; set; }

            public DispatcherWrapper(Dispatcher dispatcher)
            {
                Dispatcher = dispatcher;
            }

            /// <summary>
            /// Invoke an action on the UI thread
            /// </summary>
            public void BeginInvoke(Action action)
            {
                if (Dispatcher.CheckAccess())
                {
                    action();
                    return;
                }
                Dispatcher.BeginInvoke(action);
            }

            /// <summary>
            /// Invoke a delegate (with parameters) on the UI thread
            /// </summary>
            public void BeginInvoke(Delegate d, params object[] args)
            {
                if (Dispatcher.CheckAccess())
                {
                    d.DynamicInvoke(args);
                    return;
                }
                Dispatcher.BeginInvoke(d, args);
            }

            public bool CheckAccess()
            {
                return Dispatcher.CheckAccess();
            }
        }
    }
}
